﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using XNADota.MDX;
using System.IO;
using XNADota.Terrain;

namespace XNADota.Graphics
{
    public enum ECellType : int
    {
        None = -1,
        Surface = 0,
        Cliff = 1,
        CliffTrans = 2,
        Ramp = 3,
        SurfaceInvisible = 4,
    }

    public class CliffPaletteRendererContainer : PaletteRendererContainer
    {
        private Dictionary<string, PaletteRenderer> _CliffRenderers;
        private Dictionary<string, PaletteRenderer> _CliffTransRenderer;

        private const string _CliffPrefix = "Cliffs";
        private static readonly string[] _Layer = { "A", "B", "C" }; // three layers

        private const string _CliffTransPrefix = "CliffTrans";
        private static readonly string[] _RampLayer = { "L", "H", "X" };

        public CliffPaletteRendererContainer(Game game)
        {
            // Generate Cliffs
            _CliffRenderers = new Dictionary<string, PaletteRenderer>(128);  // actually it has 96 elements;
            _CliffTransRenderer = new Dictionary<string, PaletteRenderer>(64); // actually it has 32 elements;

            string[] CliffNames = GenerateCliffNames();
            foreach (string name in CliffNames)
            {
                MDX.Model model = game.Content.Load<MDX.Model>("Doodads\\Terrain\\Cliffs\\" + name);
                _CliffRenderers.Add(name, new CliffRenderer(game.GraphicsDevice, model));
            }

            Debug.Assert(_CliffRenderers.Count == 64);

            // Generate Cliff Ramp
            string[] CliffTransNames = GenerateCliffTransNames();
            foreach (string name in CliffTransNames)
            {
                try
                {
                    MDX.Model model = game.Content.Load<MDX.Model>("Doodads\\Terrain\\CliffTrans\\" + name);
                    _CliffTransRenderer.Add(name, new CliffRenderer(game.GraphicsDevice, model));
                }
                catch (System.Exception e)
                {
                    // allow read invalid models
                }
            }
            Debug.Assert(_CliffTransRenderer.Count == 32);
        }

        public PaletteRenderer GetPaletteRenderer(string name)
        {
            if (_CliffRenderers.ContainsKey(name))
                return _CliffRenderers[name];
            if (_CliffTransRenderer.ContainsKey(name))
                return _CliffTransRenderer[name];
            return null;
        }

        private string[] GenerateCliffNames()
        {
            List<string> ret = new List<string>();

            // all names are valid
            string value;
            for (int lb = 0; lb < 3; lb++)
                for (int lt = 0; lt < 3; lt++)
                    for (int rt = 0; rt < 3; rt++)
                        for (int rb = 0; rb < 3; rb++)
                        {
                            if (lb == lt && lt == rt && rt == rb)
                                continue;

                            value = GetName(lb, lt, rt, rb);
                            if (!ret.Contains(value))
                                ret.Add(value);
                        }

            return ret.ToArray();
        }

        private string[] GenerateCliffTransNames()
        {
            // Sorry, I don't know how to generate it, let me write all;
            string[] ret = {"AAHL", "AAHX", "AALH", "AAXH", "ABHL", "ACXH", "AHLA", "AHXA", "AHXC", "ALHA", "ALHB", "AXHA", "BALH", "BHLA",
                            "CAHX", "CXHA", "HAAL", "HAAX", "HACX", "HBAL", "HLAA", "HLAB", "HXAA", "HXCA", "LAAH", "LABH", "LHAA", "LHBA",
                            "XAAH", "XCAH", "XHAA", "XHAC"};

            //Debug.Assert(ret.Length == 32);
            for (int i = 0; i < ret.Length; i++)
                ret[i] = _CliffTransPrefix + ret[i] + "0";

            return ret;
        }

        public static string GetName(int LayerLeftBottom, int LayerLeftTop, int LayerRightTop, int LayerRightBottom)
        {
            if (LayerLeftBottom == LayerLeftTop && LayerLeftTop == LayerRightTop && LayerRightTop == LayerRightBottom)
                throw new InvalidGraphicsException("Cliff type is valid");

            int MinimumLayer = Math.Min(Math.Min(LayerLeftBottom, LayerLeftTop), Math.Min(LayerRightBottom, LayerRightTop));

            LayerLeftBottom -= MinimumLayer;
            LayerLeftTop -= MinimumLayer;
            LayerRightBottom -= MinimumLayer;
            LayerRightTop -= MinimumLayer;

            return _CliffPrefix + _Layer[LayerLeftBottom] + _Layer[LayerLeftTop] + _Layer[LayerRightTop] + _Layer[LayerRightBottom] + "0";
        }

        public static string GetName(int LayerLeftBottom, bool blb, int LayerLeftTop, bool blt, int LayerRightTop, bool brt, int LayerRightBottom, bool brb)
        {
            int MinimumLayer = Math.Min(Math.Min(LayerLeftBottom, LayerLeftTop), Math.Min(LayerRightBottom, LayerRightTop));

            LayerLeftBottom -= MinimumLayer;
            LayerLeftTop -= MinimumLayer;
            LayerRightBottom -= MinimumLayer;
            LayerRightTop -= MinimumLayer;

            return _CliffTransPrefix + GetChar(LayerLeftBottom, blb) + GetChar(LayerLeftTop, blt) + GetChar(LayerRightTop, brt) + GetChar(LayerRightBottom, brb) + "0";
        }

        public static EDirection GetDirection(int LayerLeftBottom, bool blb, int LayerLeftTop, bool blt, int LayerRightTop, bool brt, int LayerRightBottom, bool brb, ref ESide side)
        {
            EDirection result = EDirection.None;

            if (blb && blt)
                if (LayerLeftBottom > LayerLeftTop)
                {
                    side = ESide.Left;
                    result = EDirection.North;
                }
                else
                {
                    side = ESide.Right;
                    result = EDirection.South;
                }

            if (brb && brt)
                if (LayerRightBottom > LayerRightTop)
                {
                    side = ESide.Right;
                    result = EDirection.North;
                }
                else
                {
                    side = ESide.Left;
                    result = EDirection.South;
                }

            if (blb && brb)
                if (LayerLeftBottom > LayerRightBottom)
                {
                    result = EDirection.East;
                    side = ESide.Right;
                }
                else
                {
                    result = EDirection.West;
                    side = ESide.Left;
                }

            if (blt && brt)
                if (LayerLeftTop > LayerRightTop)
                {
                    side = ESide.Left;
                    result = EDirection.East;
                }
                else
                {
                    side = ESide.Right;
                    result = EDirection.West;
                }

            Debug.Assert(result != EDirection.None);
            return result;
        }
        
        private static string GetChar(int layer, bool isRamp)
        {
            if (isRamp)
                return _RampLayer[layer];
            else
                return _Layer[layer];
        }

        public static ECellType GetCellType(VertexInfo infoA, VertexInfo infoB, VertexInfo infoC, VertexInfo infoD, out string name)
        {
            ECellType cellType = ECellType.None;
            name = null;

            PaletteRendererManager manager = SceneManager.Instance.PaletteRendererManager;

            if (infoA.LayerHeight == infoB.LayerHeight && infoB.LayerHeight == infoC.LayerHeight && infoC.LayerHeight == infoD.LayerHeight)
            {
                if (infoA.SurfaceVisible[0] == false && infoB.SurfaceVisible[1] == false && infoC.SurfaceVisible[2] == false && infoD.SurfaceVisible[3] == false)
                    cellType = ECellType.SurfaceInvisible;
                else
                    cellType = ECellType.Surface;
            }
            else
            {
                // If has ramp point, it's possibly cliffTrans
                if (infoA.IsRampPoint || infoB.IsRampPoint || infoD.IsRampPoint || infoC.IsRampPoint)
                {
                    // Cliff Ramp, try to find the cliff trans
                    name = CliffPaletteRendererContainer.GetName(infoA.LayerHeight, infoA.IsRampPoint,
                                                         infoB.LayerHeight, infoB.IsRampPoint,
                                                         infoC.LayerHeight, infoC.IsRampPoint,
                                                         infoD.LayerHeight, infoD.IsRampPoint);

                    // not a cliff trans, maybe it's cliff or ramp
                    if (manager.GetPaletteRenderer(EPaletteRendererType.Cliff, name) == null)
                    {
                        // ramp is shape that four points are ramp point,otherwise it is cliff
                        int RampPointCount =
                            (infoA.IsRampPoint ? 1 : 0) +
                            (infoB.IsRampPoint ? 1 : 0) +
                            (infoC.IsRampPoint ? 1 : 0) +
                            (infoD.IsRampPoint ? 1 : 0);

                        if (RampPointCount != 4)
                        {
                            // get cliff name
                            name = CliffPaletteRendererContainer.GetName(infoA.LayerHeight, infoB.LayerHeight, infoC.LayerHeight, infoD.LayerHeight);
                            cellType = ECellType.Cliff;
                        }
                        else
                        {
                            cellType = ECellType.Ramp;
                        }
                    }
                    else
                    {
                        cellType = ECellType.CliffTrans;// cliff trans
                    }
                }
                else
                {
                    // Normal Cliff
                    name = CliffPaletteRendererContainer.GetName(infoA.LayerHeight, infoB.LayerHeight, infoC.LayerHeight, infoD.LayerHeight);
                    cellType = ECellType.Cliff;
                }
            }

            //Debug.Assert(cellType != ECellType.None);
            if (cellType == ECellType.Cliff || cellType == ECellType.CliffTrans)
                Debug.Assert(name != null && name.Length > 0 && manager.GetPaletteRenderer(EPaletteRendererType.Cliff, name) != null);

            return cellType;
        }
    }
}
